package com.anjie.powerproject.common;

import com.anjie.powerproject.entity.PreStrategy;
import com.anjie.powerproject.entity.StrategyOutput;
import com.anjie.powerproject.strategy.utils.DateUtils;
import com.github.javafaker.Faker;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;

/**
 * @author zxy
 * 用来生成mock数据的工具类
 */
@Component
public class MockDataUtil {

    @Autowired
    Faker faker;

    private static NumberFormat nf = NumberFormat.getNumberInstance();

    private final static List<String> deviceSupply = Arrays.asList("地源热泵主机", "地埋管", "水蓄能", "太阳能");

    private final static List<String> deviceStorage = Arrays.asList("水蓄能");

    private final static List<String> deviceConsume = Arrays.asList("地板辐射", "生活热水箱");

    static {
        nf.setMaximumFractionDigits(2);
    }

    /**
     * 生成指定size的double列表
     * @param count 数组长度
     * @return double list
     */
    private List<Double> buildDoubleList(int count, double start, double end) {
        return ThreadLocalRandom.current().doubles(count, start, end).mapToObj(nf::format).map(s -> s.replaceAll(",", "")).map(Double::parseDouble).collect(Collectors.toList());
    }


    public List<Double> buildDataList(int count) {
        return buildDoubleList(count, 500, 2000);
    }

    // double -> BigDecimal
    public List<BigDecimal> buildBigDecimalDataList(int count) {
        List<Double> DList = buildDoubleList(count, 500, 2000);
        List<BigDecimal> res = new ArrayList<>();
        for (Double d : DList) {
            res.add(BigDecimal.valueOf(d));
        }
        return res;
    }

    public List<List<String>> buildDataBody(List<String> columns, Date startTime, Date endTime) throws ParseException {
        List<List<String>> data = new ArrayList<>();
        Calendar start = DateUtils.dateToCalender(startTime);
        Calendar end = DateUtils.dateToCalender(endTime);

        for (Calendar d = start; d.compareTo(end) <= 0; d.add(Calendar.DATE, 1)) {
            List<String> row = new ArrayList<>();

            String time = DateUtils.ymdFormat.format(start.getTime());
            // mock 时间
            row.add(time);
            
            // mock 能量
            row.add(buildDouble(1000, 2000).toString());
            
            // mock天气数据
            Double high = buildDouble(25, 35);
            Double low = buildDouble(15, high);
            Double avg = (high + low) / 2.0;
            row.add(high.toString());
            row.add(low.toString());
            row.add(nf.format(avg));

            row.add(buildDouble(1, 10).toString());
            Double highHum = buildDouble(60, 90);
            Double lowHum = buildDouble(40, highHum);
            row.add(highHum.toString());
            row.add(lowHum.toString());

            row.add(chooseOne(Arrays.asList("0", "1", "2")));

            // 时间数据
            DateUtils.TimeProperty property = DateUtils.getPropertyByDateStr(time);
            row.add(property.getHoliday());
            row.add(property.getWeekOfYear());
            row.add(property.getSeason());
            row.add(property.getMonth());
            row.add(property.getDay());
            // 星期数独热码
            for (int i = 1; i <= 7; i++) {
                if (String.valueOf(i).equals(property.getDayOfWeek())) {
                    row.add("1");
                } else {
                    row.add("0");
                }
            }
            // 供冷季、供热季
            int month = Integer.parseInt(property.getMonth());
            int day = Integer.parseInt(property.getDay());
            if ((month > 10) || (month < 4) || (month == 10 && day >= 18) || (month == 4 && day <= 1)) {
                row.add("1");
            } else {
                row.add("0");
            }
            if ((month > 6 && month < 9) || (month == 6 && day >= 8) || (month == 9 && day <= 22)) {
                row.add("1");
            } else {
                row.add("0");
            }

            data.add(row);
        }

        return data;
    }

    /**
     * 生成单个double
     * @return double
     */
    private Double buildDouble(double start, double end) {
        double v = ThreadLocalRandom.current().nextDouble(start, end);
        return Double.parseDouble(nf.format(v).replaceAll(",", ""));
    }

    public Double buildScore() {
        return buildDouble(80, 100);
    }

    private String chooseOne(List<String> list) {
        int i = ThreadLocalRandom.current().nextInt(0, list.size());
        return list.get(i);
    }

    private String buildSupplyStrategyInfo(String time) {
        String from = chooseOne(deviceSupply);
        String to = chooseOne(deviceConsume);

        return String.format("%s %s 放能到 %s", time, from, to);
    }

    private String buildStorageStrategyInfo(String time) {
        String from = chooseOne(deviceSupply);
        String to = chooseOne(deviceStorage);

        if (Objects.equals(from, to)) {
            return buildStorageStrategyInfo(time);
        }

        return String.format("%s %s 蓄能到 %s", time, from, to);
    }

    private String buildInfo(Date startTime, Date endTime) {
        String start = DateUtils.hmFormat.format(startTime);
        String end = DateUtils.hmFormat.format(endTime);

        int i = ThreadLocalRandom.current().nextInt(1, 3);
        if (i == 1) {
            return buildSupplyStrategyInfo(start + "-" + end);
        } else {
            return buildStorageStrategyInfo(start + "-" + end);
        }
    }

    private List<Date> splitDate(Date start, Date end, int count) {
        int offset = 86400 / count;
        List<Date> dateList = new ArrayList<>();

        Calendar calendar = DateUtils.dateToCalender(start);

        for (int i = 1; i < count; i++) {
            calendar.add(Calendar.SECOND, offset);
            dateList.add(calendar.getTime());
        }
        dateList.add(end);

        return dateList;
    }

    private List<PreStrategy> buildPreStrategyListOneDay(Date time) {
        List<PreStrategy> preStrategies = new ArrayList<>();

        Calendar calendar = DateUtils.dateToCalender(time);
        calendar.set(Calendar.HOUR, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);

        Date today = calendar.getTime();
        calendar.add(Calendar.DATE, 1);
        Date tomorrow = calendar.getTime();

        // 随机看分为几段，最多分5段
        int split = ThreadLocalRandom.current().nextInt(2, 6);
        List<Date> limitDate = splitDate(today, tomorrow, split);

        Date start = today;
        for (Date date : limitDate) {
            Date between = faker.date().between(start, date);

            PreStrategy strategy = new PreStrategy();
            strategy.setStartTime(start);
            strategy.setEndTime(between);
            strategy.setInfo(buildInfo(start, between));

            preStrategies.add(strategy);

            start = between;
        }

        PreStrategy strategy = new PreStrategy();
        strategy.setStartTime(start);
        strategy.setEndTime(tomorrow);
        strategy.setInfo(buildInfo(start, tomorrow));

        preStrategies.add(strategy);

        return preStrategies;
    }

    public List<List<PreStrategy>> buildPreStrategyList(Date startTime, Date endTime) {
        List<List<PreStrategy>> preStrategyList = new ArrayList<>();

        Calendar startCalendar = DateUtils.dateToCalender(startTime);
        startCalendar.set(Calendar.HOUR, 0);
        startCalendar.set(Calendar.MINUTE, 0);
        startCalendar.set(Calendar.SECOND, 0);

        Calendar endCalendar = DateUtils.dateToCalender(endTime);
        endCalendar.set(Calendar.HOUR, 0);
        endCalendar.set(Calendar.MINUTE, 0);
        endCalendar.set(Calendar.SECOND, 0);

        for (Date date = startCalendar.getTime(); startCalendar.compareTo(endCalendar) <= 0; startCalendar.add(Calendar.DATE, 1)) {
            preStrategyList.add(buildPreStrategyListOneDay(date));
        }

        return preStrategyList;
    }

    private List<StrategyOutput> buildStrategyOutputListOneDay(Date time) {
        List<StrategyOutput> strategyOutputs = new ArrayList<>();

        Calendar calendar = DateUtils.dateToCalender(time);
        calendar.set(Calendar.HOUR, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);

        Date today = calendar.getTime();
        calendar.add(Calendar.DATE, 1);
        Date tomorrow = calendar.getTime();

        // 随机看分为几段，最多分5段
        int split = ThreadLocalRandom.current().nextInt(2, 6);
        List<Date> limitDate = splitDate(today, tomorrow, split);

        Date start = today;
        for (Date date : limitDate) {
            Date between = faker.date().between(start, date);

            StrategyOutput strategy = new StrategyOutput();
            strategy.setStartTime(start);
            strategy.setEndTime(between);
            strategy.setInfo(buildInfo(start, between));

            strategyOutputs.add(strategy);

            start = between;
        }

        StrategyOutput strategy = new StrategyOutput();
        strategy.setStartTime(start);
        strategy.setEndTime(tomorrow);
        strategy.setInfo(buildInfo(start, tomorrow));

        strategyOutputs.add(strategy);

        return strategyOutputs;
    }

    public List<List<StrategyOutput>> buildStrategyOutputList(Date startTime, Date endTime) {
        List<List<StrategyOutput>> strategyOutputList = new ArrayList<>();

        Calendar startCalendar = DateUtils.dateToCalender(startTime);
        startCalendar.set(Calendar.HOUR, 0);
        startCalendar.set(Calendar.MINUTE, 0);
        startCalendar.set(Calendar.SECOND, 0);

        Calendar endCalendar = DateUtils.dateToCalender(endTime);
        endCalendar.set(Calendar.HOUR, 0);
        endCalendar.set(Calendar.MINUTE, 0);
        endCalendar.set(Calendar.SECOND, 0);

        for (Date date = startCalendar.getTime(); startCalendar.compareTo(endCalendar) <= 0; startCalendar.add(Calendar.DATE, 1)) {
            strategyOutputList.add(buildStrategyOutputListOneDay(date));
        }

        return strategyOutputList;
    }

}
